from flask import (Blueprint, render_template,
                   request, jsonify, current_app,
                   make_response, session, redirect, g, url_for)
import string
import random
from exts import mail, cache, db
from utils import restful
import time
# 加密
from hashlib import md5
# 图片 验证码保存
from io import BytesIO
from utils.captcha import Captcha
from .forms import RegisterForm, LoginForm, UploadImageForm, EditProfileForm, PublicPostForm, PublicCommentForm
# 模型
from models.auth import UserModel, Permission
from models.post import BoardModel, PostModel, CommentModel, BannerModel
from .decorators import login_required
from flask_avatars import Identicon
import os
# 分页
from flask_paginate import get_page_parameter, Pagination
# jwt
from flask_jwt_extended import create_access_token

bp = Blueprint('front', __name__, url_prefix='/')


# 钩子函数before_request
@bp.before_request
def front_before_request():
    if 'user_id' in session:
        user_id = session.get('user_id')
        user = db.session.get(UserModel, user_id)
        if user:
            setattr(g, 'user', user)


# 上下文处理器
@bp.context_processor
def front_context_processor():
    if hasattr(g, 'user'):
        return {'user': g.user}
    return {}


# @bp.get('/email/captcha')
# def email_captcha():
#     email = request.args.get('email')
#     if not email:
#         return jsonify({'code': 400, 'message': '请先传入邮箱'})
#     # 随机生成6为数字
#     source = list(string.digits)
#     captcha = "".join(random.sample(source, 6))
#     message = Message(subject="[小小传课]注册验证码", recipients=[email], body=f"[小小传课]您的注册验证码为:{captcha}")
#     try:
#         mail.send(message)
#     except Exception as e:
#         print("邮件发送失败!")
#         print(e)
#         return jsonify({'code': 500, 'message': '邮件发送失败!'})
#     return jsonify({'code': 200, 'message': '邮件发送成功!'})


@bp.get('/email/captcha')
def email_captcha():
    email = request.args.get('email')
    if not email:
        return restful.params_error(message="请先传入邮箱!")
    # 随机生成6为数字
    source = list(string.digits)
    captcha = "".join(random.sample(source, 6))
    subject = "[小小传课]注册验证码"
    body = f"[小小传课]您的注册验证码为:{captcha}"
    current_app.celery.send_task('send_mail', (email, subject, body))
    cache.set(email, captcha)
    return restful.ok(message="邮件发送成功!")


@bp.route("/graph/captcha")
def graph_captcha():
    captcha, image = Captcha.gene_graph_captcha()
    # 将验证码存放到缓存中
    # key, value
    # bytes
    key = md5((captcha + str(time.time())).encode('utf-8')).hexdigest()
    cache.set(key, captcha)
    # 将 out 的文件指针指向最前的位置
    out = BytesIO()
    image.save(out, "png")
    # 把 out 的文件指针指向最前的位置
    out.seek(0)
    resp = make_response(out.read())
    resp.content_type = "image/png"
    resp.set_cookie("_graph_captcha_key", key, max_age=3600)
    return resp


@bp.route('/')
def index():  # put application's code here
    sort = request.args.get('st', type=int, default=1)
    board_id = request.args.get('bd', type=int, default=None)
    boards = BoardModel.query.order_by(BoardModel.priority.desc()).all()
    post_query = None
    if sort == 1:
        post_query = PostModel.query.order_by(PostModel.create_time.desc())
    else:
        # 根据评论排序
        post_query = db.session.query(PostModel).outerjoin(CommentModel).group_by(PostModel.id).order_by(
            db.func.count(CommentModel.id).desc(), PostModel.create_time.desc())

    page = request.args.get(get_page_parameter(), type=int, default=1)
    start = (page - 1) * current_app.config['PER_PAGE_COUNT']
    end = start + current_app.config['PER_PAGE_COUNT']

    if board_id:
        post_query = post_query.filter(PostModel.board_id == board_id)

    # 修正：对查询对象使用slice方法
    posts = post_query.slice(start, end).all()
    # 保持total作为总数统计
    total = post_query.count()
    pagination = Pagination(bs_version=3, page=page, total=total)
    banners = BannerModel.query.order_by(BannerModel.priority.desc()).all()
    context = {
        'boards': boards,
        'posts': posts,
        'pagination': pagination,
        'st': sort,
        'bd': board_id,
        'banners': banners
    }
    return render_template('front/index.html', **context)


@bp.route('/login/', methods=['GET', 'POST'])
def login():
    if request.method == 'GET':
        return render_template('front/login.html')
    else:
        form = LoginForm(request.form)
        if form.validate():
            email = form.email.data
            password = form.password.data
            remember = form.remember.data
            user = UserModel.query.filter_by(email=email).first()
            if not user:
                return restful.params_error(message="邮箱或密码错误!")
            if not user.check_password(password):
                return restful.params_error(message="邮箱或密码错误!")
            session['user_id'] = user.id
            # 判断是否是管理员
            token = ""
            permissions = []
            if user.is_staff:
                token = create_access_token(identity=user.id)
                for attr in dir(Permission):
                    if not attr.startswith("_"):
                        permission = getattr(Permission, attr)
                        if user.has_permission(permission):
                            permissions.append(attr.lower())
            if remember == 1:
                session.permanent = True
            user_dict = user.to_dict()
            user_dict['permissions'] = permissions
            return restful.ok(message="登录成功!", data={'token': token, 'user': user_dict})

        else:
            message = form.messages[0]
            return restful.params_error(message=message)


@bp.route('/register/', methods=['GET', 'POST'])
def register():
    if request.method == 'GET':
        return render_template('front/register.html')
    else:
        form = RegisterForm(request.form)
        if form.validate():
            email = form.email.data
            username = form.username.data
            password = form.password.data
            email_hash = md5(email.encode('utf-8')).hexdigest()
            identicon = Identicon()
            filenames = identicon.generate(text=email_hash)  # 小中大 0,1,2
            avatar = filenames[2]
            user = UserModel(email=email, username=username, avatar=avatar)
            user.password = password
            db.session.add(user)
            db.session.commit()
            return restful.ok(message="注册成功!")
        else:
            message = form.messages[0]
            return restful.params_error(message=message)


@bp.route('/logout/')
def logout():
    session.clear()
    return redirect('/')


@bp.route('/setting/')
@login_required
def setting():
    email_hash = md5(g.user.email.encode('utf-8')).hexdigest()
    return render_template('front/setting.html', email_hash=email_hash)


@bp.post('/avatar/upload')
@login_required
def upload_avatar():
    form = UploadImageForm(request.files)
    if form.validate():
        image = form.image.data
        ext = image.filename.split('.')[-1]
        filename = md5((g.user.email + str(time.time())).encode('utf-8')).hexdigest() + '.' + ext
        image_path = os.path.join(current_app.config['AVATARS_SAVE_PATH'], filename)
        image.save(image_path)
        # 图像上传后立刻修改
        g.user.avatar = filename
        db.session.commit()
        return restful.ok(data={'avatar': filename})
    else:
        message = form.messages[0]
        return restful.params_error(message=message)


@bp.post('profile/edit')
@login_required
def edit_profile():
    form = EditProfileForm(request.form)
    if form.validate():
        signature = form.signature.data
        g.user.signature = signature
        db.session.commit()
        return restful.ok(message="个性签名,修改成功!")
    else:
        message = form.messages[0]
        return restful.params_error(message=message)


@bp.route('/post/public/', methods=['GET', 'POST'])
def public_post():
    if request.method == 'GET':
        boards = BoardModel.query.order_by(BoardModel.priority.desc()).all()
        return render_template('front/public_post.html', boards=boards)
    else:
        form = PublicPostForm(request.form)
        if form.validate():
            title = form.title.data
            content = form.content.data
            board_id = form.board_id.data
            try:
                board = db.session.get(BoardModel, board_id)
            except Exception as e:
                return restful.params_error(message="板块不存在!")
            post_model = PostModel(title=title, content=content, board=board, author=g.user)
            db.session.add(post_model)
            db.session.commit()
            return restful.ok(message="帖子发布成功!", data={
                "id": post_model.id
            })
        else:
            message = form.messages[0]
            return restful.params_error(message=message)


# 富文本上传图片
@bp.post('/post/image/upload')
@login_required
def upload_post_image():
    form = UploadImageForm(request.files)
    if form.validate():
        image = form.image.data
        ext = image.filename.split('.')[-1]
        filename = md5((g.user.email + str(time.time())).encode('utf-8')).hexdigest() + '.' + ext
        image_path = os.path.join(current_app.config['POST_IMAGE_SAVE_PATH'], filename)
        image.save(image_path)
        return jsonify({
            "errno": 0, "data": [{
                "url": url_for('media.get_post_image', filename=filename),
                "alt": filename,
                "href": ""
            }]
        })
    else:
        message = form.messages[0]
        return restful.params_error(message=message)


# 帖子详情页面
@bp.get("/post/detail/<int:post_id>")
def post_detail(post_id):
    try:
        post_model = PostModel.query.get(post_id)
    except:
        return "404"
    comment_count = CommentModel.query.filter_by(post_id=post_id).count()
    context = {
        "post": post_model,
        "comment_count": comment_count,
    }
    return render_template("front/post_detail.html", **context)


# 发布评论
@bp.post('/comment')
@login_required
def public_comment():
    form = PublicCommentForm(request.form)
    if form.validate():
        post_id = form.post_id.data
        content = form.content.data
        try:
            post_model = db.session.get(PostModel, post_id)
        except Exception as e:
            return restful.params_error(message="帖子不存在!")
        comment = CommentModel(content=content, post_id=post_id, author_id=g.user.id)
        db.session.add(comment)
        db.session.commit()
        return restful.ok(message="评论发布成功!")
    else:
        message = form.messages[0]
        return restful.params_error(message=message)


@bp.get('/cms')
def cms():
    return render_template('cms/index.html')